package sysMemu

import (
	"context"
	"errors"
	v1 "gf-admin/api/system/v1"
	"gf-admin/internal/consts"
	"gf-admin/internal/dao"
	"gf-admin/internal/logic/casbin"
	"gf-admin/internal/model"
	"gf-admin/internal/model/do"
	"gf-admin/internal/service"
	liberr "gf-admin/utility/lib"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"
)

type sSysMenu struct {
}

func New() *sSysMenu {
	return &sSysMenu{}
}

func init() {
	service.RegisterSysMenu(New())
}

//获取菜单 这里是菜单管理使用的
func (s *sSysMenu) GetMenusAllMenu(ctx context.Context) (out []*model.SysMenuList, errs error) {
	allMenus, err := s.GetIsMenuList(ctx, true)
	if err != nil {
		return nil, err
	}
	//二次处理
	menusList := s.GetRoleMenuTree(allMenus, 0)
	return menusList, nil
}

//获取菜单 这里是给角色管理里面设置权限使用的
func (s *sSysMenu) GetRoleAllMenu(ctx context.Context) (out []*model.SysMenuList, errs error) {
	allMenus, err := s.GetIsMenuList(ctx, false)
	if err != nil {
		return nil, err
	}
	//二次处理
	menusList := s.GetRoleMenuTree(allMenus, 0)
	return menusList, nil
}

func (s *sSysMenu) GetRoleMenuTree(menus []*model.SysMenuList, pid uint) []*model.SysMenuList {
	returnList := make([]*model.SysMenuList, 0, len(menus))
	for _, menu := range menus {
		if menu.Pid == pid {
			mChildren := s.GetRoleMenuTree(menus, menu.Id)
			menu.Children = mChildren
			returnList = append(returnList, menu)
		}
	}
	return returnList
}

//获取菜单,这里是给左侧菜单栏使用的
func (s *sSysMenu) GetAllMenuList(ctx context.Context) (out []*model.UserMenus, errs error) {
	allMenus, err := s.GetIsMenuList(ctx, false)
	if err != nil {
		return nil, err
	}
	//开始处理菜单层级数据结构
	menus := make([]*model.UserMenus, len(allMenus))
	for k, v := range allMenus {
		var menu *model.UserMenu
		menu = s.SetMenuData(menu, v)
		menus[k] = &model.UserMenus{UserMenu: menu}
	}
	//二次处理
	menusList := s.GetMenusTree(menus, 0)
	return menusList, nil
}

func (s *sSysMenu) GetIsMenuList(ctx context.Context, isFlag bool) (out []*model.SysMenuList, errs error) {
	sysMenuList, err := s.GetDbMenuList(ctx)
	if err != nil {
		return nil, err
	}
	//筛选出所有的目录和菜单
	var allMenus = make([]*model.SysMenuList, 0, len(sysMenuList))
	if isFlag {
		return sysMenuList, nil
	}
	for _, v := range sysMenuList {
		if v.MenuType == consts.Directory || v.MenuType == consts.Menu {
			allMenus = append(allMenus, v)

		}
	}
	return allMenus, nil
}

// GetMenuList 获取所有菜单
func (s *sSysMenu) GetDbMenuList(ctx context.Context) (list []*model.SysMenuList, err error) {
	var sysMenuList = make([]*model.SysMenuList, 0)
	errs := dao.SysMenu.Ctx(ctx).Order("weigh desc,id asc").Scan(&sysMenuList)
	if errs != nil {
		return nil, errors.New("查询" + dao.SysMenu.Table() + "表失败:" + errs.Error())
	}
	if len(sysMenuList) == 0 {
		return nil, errors.New("菜单栏数据为空")
	}
	return sysMenuList, nil
}

func (s *sSysMenu) GetMenusTree(menus []*model.UserMenus, pid uint) []*model.UserMenus {
	returnList := make([]*model.UserMenus, 0, len(menus))
	for _, menu := range menus {
		if menu.Pid == pid {
			mChildren := s.GetMenusTree(menus, menu.Id)
			menu.Children = mChildren
			returnList = append(returnList, menu)
		}
	}
	return returnList
}

func (s *sSysMenu) SetMenuData(menu *model.UserMenu, entity *model.SysMenuList) *model.UserMenu {
	menu = &model.UserMenu{
		Id:        entity.Id,
		Pid:       entity.Pid,
		Name:      gstr.CaseCamelLower(gstr.Replace(entity.Name, "/", "_")),
		Component: entity.Component,
		Path:      entity.Path,
		MenuMeta: &model.MenuMeta{
			Icon:        entity.Icon,
			Title:       entity.Title,
			IsLink:      "",
			IsHide:      entity.IsHide == 1,
			IsKeepAlive: entity.IsCached == 1,
			IsAffix:     entity.IsAffix == 1,
			IsIframe:    entity.IsIframe == 1,
		},
	}
	if menu.MenuMeta.IsIframe || entity.IsLink == 1 {
		menu.MenuMeta.IsLink = entity.LinkUrl
	}
	return menu
}

// Tree 将菜单用递归的形式加工成成品数据
/*func (s *sSysMenu) Tree(menus []*model.SysMenu, pid uint) []*model.SysMenu {
	var tree []*model.SysMenu
	for _, menuItem := range menus {
		if menuItem.Pid == pid {
			var children []*model.SysMenu
			//调用自身 递归寻找自己的子菜单
			child := s.Tree(menus, menuItem.Id)
			children = append(children, child...)
			//找完子菜单后将自身存入到树中
			menuItem.Children = children
			tree = append(tree, menuItem)
		}
	}
	return tree
}*/

// GetIsButtonList 获取所有按钮isMenu=2 菜单列表

func (s *sSysMenu) GetIsButtonList(ctx context.Context) ([]*model.SysMenuList, error) {
	list, err := s.GetDbMenuList(ctx)
	if err != nil {
		return nil, err
	}
	var gList = make([]*model.SysMenuList, 0, len(list))
	for _, v := range list {
		if v.MenuType == 2 {
			gList = append(gList, v)
		}
	}
	return gList, nil
}

func (s *sSysMenu) Get(ctx context.Context, id uint) (rule *model.SysMenuList, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		err = dao.SysMenu.Ctx(ctx).WherePri(id).Scan(&rule)
		liberr.ErrIsNil(ctx, err, "获取菜单失败")
	})
	return
}

// Add 添加菜单
func (s *sSysMenu) Add(ctx context.Context, req *v1.RuleAddReq) (err error) {
	if s.menuNameExists(ctx, req.Name, 0) {
		err = gerror.New("接口规则已经存在")
		return
	}
	err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		err = g.Try(ctx, func(ctx context.Context) {
			//菜单数据
			data := do.SysMenu{
				Pid:       req.Pid,
				Name:      req.Name,
				Title:     req.Title,
				Icon:      req.Icon,
				Remarks:   req.Remark,
				MenuType:  req.MenuType,
				Weigh:     req.Weigh,
				IsHide:    req.IsHide,
				Path:      req.Path,
				Component: req.Component,
				IsLink:    req.IsLink,
				IsIframe:  req.IsIframe,
				IsCached:  req.IsCached,
				Redirect:  req.Redirect,
				IsAffix:   req.IsAffix,
				LinkUrl:   req.LinkUrl,
			}
			ruleId, e := dao.SysMenu.Ctx(ctx).TX(tx).InsertAndGetId(data)
			liberr.ErrIsNil(ctx, e, "添加菜单失败")
			e = service.SysRole().BindRoleRule(ctx, ruleId, req.Roles)
			liberr.ErrIsNil(ctx, e, "添加菜单失败")
		})
		return err
	})

	return
}

// 检查菜单规则是否存在
func (s *sSysMenu) menuNameExists(ctx context.Context, name string, id uint) bool {
	m := dao.SysMenu.Ctx(ctx).Where("name=?", name)
	if id != 0 {
		m = m.Where("id!=?", id)
	}
	c, err := m.Fields(dao.SysMenu.Columns().Id).Limit(1).One()
	if err != nil {
		g.Log().Error(ctx, err)
		return false
	}
	return !c.IsEmpty()
}

// DeleteMenuByIds 删除菜单
func (s *sSysMenu) DeleteMenuByIds(ctx context.Context, ids []int) (err error) {
	var list []*model.SysMenuList

	list, err = s.GetIsMenuList(ctx, false)
	if err != nil {
		return
	}
	childrenIds := make([]int, 0, len(list))
	for _, id := range ids {
		rules := s.FindSonByParentId(list, gconv.Uint(id))
		for _, child := range rules {
			childrenIds = append(childrenIds, gconv.Int(child.Id))
		}
	}
	ids = append(ids, childrenIds...)
	err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		return g.Try(ctx, func(ctx context.Context) {
			_, err = dao.SysMenu.Ctx(ctx).Where("id in (?)", ids).Delete()
			liberr.ErrIsNil(ctx, err, "删除失败")
			//删除权限
			enforcer, err := casbin.CasbinEnforcer(ctx)
			liberr.ErrIsNil(ctx, err)
			for _, v := range ids {
				_, err = enforcer.RemoveFilteredPolicy(1, gconv.String(v))
				liberr.ErrIsNil(ctx, err)
			}
		})
	})
	return
}

func (s *sSysMenu) FindSonByParentId(list []*model.SysMenuList, pid uint) []*model.SysMenuList {
	children := make([]*model.SysMenuList, 0, len(list))
	for _, v := range list {
		if v.Pid == pid {
			children = append(children, v)
			fChildren := s.FindSonByParentId(list, v.Id)
			children = append(children, fChildren...)
		}
	}
	return children
}

func (s *sSysMenu) Update(ctx context.Context, req *v1.RuleUpdateReq) (err error) {
	if s.menuNameExists(ctx, req.Name, req.Id) {
		err = gerror.New("接口规则已经存在")
		return
	}
	err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		err = g.Try(ctx, func(ctx context.Context) {
			//菜单数据
			data := do.SysMenu{
				Pid:        req.Pid,
				Name:       req.Name,
				Title:      req.Title,
				Icon:       req.Icon,
				Conditions: req.Condition,
				Remarks:    req.Remark,
				MenuType:   req.MenuType,
				Weigh:      req.Weigh,
				IsHide:     req.IsHide,
				Path:       req.Path,
				Component:  req.Component,
				IsLink:     req.IsLink,
				IsIframe:   req.IsIframe,
				IsCached:   req.IsCached,
				Redirect:   req.Redirect,
				IsAffix:    req.IsAffix,
				LinkUrl:    req.LinkUrl,
			}
			_, e := dao.SysMenu.Ctx(ctx).TX(tx).WherePri(req.Id).Update(data)
			liberr.ErrIsNil(ctx, e, "添加菜单失败")
			e = s.UpdateRoleRule(ctx, req.Id, req.Roles)
			liberr.ErrIsNil(ctx, e, "添加菜单失败")
		})
		return err
	})

	return
}

func (s *sSysMenu) UpdateRoleRule(ctx context.Context, ruleId uint, roleIds []uint) (err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		enforcer, e := casbin.CasbinEnforcer(ctx)
		liberr.ErrIsNil(ctx, e)
		//删除旧权限
		_, e = enforcer.RemoveFilteredPolicy(1, gconv.String(ruleId))
		liberr.ErrIsNil(ctx, e)
		// 添加新权限
		roleIdsStrArr := gconv.Strings(roleIds)
		for _, v := range roleIdsStrArr {
			_, e = enforcer.AddPolicy(v, gconv.String(ruleId), "All")
			liberr.ErrIsNil(ctx, e)
		}
	})
	return
}
